import { Camera, Component, Label, Node, Prefab, _decorator, math, view } from "cc";
import { EnumUtil } from "../../game/lib/kgame/util/EnumUtil";
import { Main } from "../Main";
import { Conn } from "../conn/Conn";
import { UIMsg } from "../ui/UIMsg";
import { Background } from "./Background";
import { GameCamera } from "./GameCamera";
import { FoodMgr } from "./entity/FoodMgr";
import { SnakeMgr } from "./entity/SnakeMgr";
const { ccclass, property } = _decorator;

/** 游戏层级 */
export enum GameLayer {
    food,
    snake,
    name,
}

/**
 * 游戏管理器
 */
@ccclass("GameMgr")
export class GameMgr extends Component {

    static ins: GameMgr;

    @property(Prefab)
    labNamePrefab: Prefab;
    @property(Prefab)
    headPrefab: Prefab;
    @property(Prefab)
    bodyPrefab: Prefab;
    @property(Prefab)
    foodPrefab: Node;
    /** 游戏背景 */
    @property(Background)
    background: Background;
    /** 游戏层级节点 */
    @property(Node)
    gameLayer: Node;
    @property(Camera)
    camera: Camera;
    @property(Label)
    labScore: Label;
    @property(UIMsg)
    uiMsg: UIMsg;

    /** 缩放 */
    scale: number = 1;

    /** 玩家id */
    pid: number;
    /** 玩家蛇id */
    snakeId: number;

    /** 游戏相机 */
    gameCamera: GameCamera;
    /** 游戏层级 */
    gameLayerMap = new Map<GameLayer, Node>();
    /** 蛇管理器 */
    snakeMgr: SnakeMgr;
    /** 食物管理器 */
    foodMgr: FoodMgr;
    /** 焦点id */
    focusId: number;

    protected onLoad(): void {
        (window as any)["GameMgr"] = this;
        GameMgr.ins = this;
    }

    protected start(): void {
        this.gameCamera = new GameCamera(this.camera);

        //游戏层级初始化
        EnumUtil.getNumEnumItems(GameLayer).forEach(item => {
            const [name, layer] = item;
            const node = new Node(name);
            this.gameLayerMap.set(layer, node);
            this.gameLayer.addChild(node);
        });

        this.snakeMgr = new SnakeMgr(this.gameLayerMap.get(GameLayer.snake));
        this.foodMgr = new FoodMgr(this.gameLayerMap.get(GameLayer.food));

        Conn.on(KProtoId.joinGame, data => {
            const { width, height } = data.worldSize;
            this.pid = data.pid;
            this.background.updateBg({ x: -width / 2, y: -height / 2, width, height });
            Main.ins.gameUI.active = true;
            Main.ins.menuUI.active = false;
            Conn.sendAsync(KProtoId.ready).then(data => {
                this.snakeId = data.snakeId;
            });
        });

        Conn.on(KProtoId.focus, data => {
            this.focusId = data.id;
        });

        Conn.on(KProtoId.snakeInfo, data => {
            data.snakes.forEach(snake => {
                if (data.type === 1) {
                    this.snakeMgr.addSnake(snake);
                } else if (data.type === 2) {
                    this.snakeMgr.updateSnake(snake);
                }
            });
        });

        Conn.on(KProtoId.entityAdd, data => {

            data.snakes?.forEach(snake => {
                this.snakeMgr.onEntitySnakeData(1, snake);
            });

            data.foods?.forEach(food => {
                this.foodMgr.addFood(food);
            });

        });

        Conn.on(KProtoId.entityUpdate, data => {
            data.snakes?.forEach(snake => {
                this.snakeMgr.onEntitySnakeData(2, snake);

            })
        });

        Conn.on(KProtoId.foodEaten, data => {
            data.foods.forEach(food => {
                this.foodMgr.onFoodEaten(food);
            });
        });

        Conn.on(KProtoId.entityRemove, data => {
            data.ids.forEach(id => {
                this.snakeMgr.onEntityRemove(id);
                this.foodMgr.removeFood(id);
            });
        });


        Conn.on(KProtoId.msg_add, data => {
            data.msgs.forEach(msg => {
                this.uiMsg.addMsg(msg);
            })
        });
    }

    protected update(dt: number): void {
        if (this.focusId) {
            const focus = this.snakeMgr.bodyMap.get(this.focusId);
            this.gameCamera.focus = focus?.node;
            if (focus) {
                const { width, height } = view.getVisibleSize();
                const size = focus.size * 25;
                this.scale = Math.sqrt((width * height) / (size * size));
                this.scale *= view.getDesignResolutionSize().height / height;
            }
        } else {
            this.gameCamera.focus = null;
        }
    }

    protected lateUpdate(dt: number): void {
        this.gameCamera.update(dt);
        const scale = math.lerp(this.node.scale.x, this.scale, dt * 10);
        this.node.setScale(scale, scale);
    }

}